/**
 * elancom Corporation copy right 2012 - 2015  
 *   
 * $Header: https://192.168.1.224/svn/elancom/product/elancom-platform/trunk/src/main/java/com/elancom/framework/dao/Select.java 1034 2012-07-02 03:09:12Z lanzhouxue $
 * $Id: Select.java 1034 2012-07-02 03:09:12Z lanzhouxue $
 * $Author: lanzhouxue $
 * $Date: 2012-07-02 11:09:12 +0800 (周一, 02 七月 2012) $
 * $Revision: 1034 $
 */
package com.elancom.framework.dao;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.elancom.framework.Constants;
import com.elancom.framework.commons.limit.Limit;
import com.elancom.framework.commons.util.StringUtils;
import com.elancom.framework.dao.internal.SelectCompiledImpl;
import com.elancom.framework.entity.DeletedSupport;

/**
 * @author elancom
 * @date May 3, 2012
 */
public class Select extends AbstractStructureSupport<Select> {
	public static int MAX_RESULTS = Integer.MAX_VALUE;

	private String countStringCompile = null;

	private int curPage = -1;

	/**
	 * 是否忽略deleted条件
	 */
	private transient boolean filterDeleted = true;

	private int firstResult;

	private transient boolean fromed = false;

	private transient StringBuilder fromString = new StringBuilder("");

	private boolean isComplie = false;

	private transient StringBuffer joinString = new StringBuffer("");

	private transient Logger logger = LoggerFactory.getLogger(Select.class);

	private transient Class<?> mainEntityClass = null;

	private transient String masterEntityAlias = null;

	private transient int maxResults = MAX_RESULTS;

	private transient boolean ordered;

	private transient StringBuilder orderString = new StringBuilder("");

	private int pageSize = 50;

	private transient boolean selected = false;

	private transient StringBuilder selectString = new StringBuilder("");

	private String selectStringCompile = null;

	public Select() {
		super();
	}

	public Select(Class<?> clazz) {
		super();
		from(clazz);
	}

	public Select(Class<?> clazz, int curPage, int pageSize) {
		super();
		this.curPage = curPage;
		this.pageSize = pageSize;
	}

	public Select(Class<?> clazz, String alias) {
		super();
		from(clazz, alias);
	}

	/**
	 * 只是在构建HQL的时候调用,这样在deleted的设置不会影响用户设置的条件
	 * 
	 * @param sbuf
	 */
	private void appendDeleted(StringBuilder sbuf) {
		boolean useDeleted = filterDeleted;
		if (!useDeleted) {
			return;
		}

		if (mainEntityClass != null) {
			if (DeletedSupport.class.isAssignableFrom(mainEntityClass)) {
				useDeleted = true;
			} else {
				useDeleted = false;
			}
		}

		if (useDeleted) {
			if (whereString.length() == 0) {
				sbuf.append("where ");
			} else {
				sbuf.append(" and ");
			}

			if (masterEntityAlias != null && masterEntityAlias.trim().length() != 0) {
				sbuf.append(masterEntityAlias).append(".");
			}
			sbuf.append("deleted = ").append(Constants.INT_STATE_FALSE);
		}
	}

	public String buildCountString() {
		StringBuilder countString = new StringBuilder();
		countString.append("select ");

		countString.append("count(");
		if (isUseDistinct()) {
			countString.append("distinct ");
		}
		if (masterEntityAlias != null && masterEntityAlias.trim().length() != 0) {
			countString.append(masterEntityAlias).append(".");
		}
		countString.append("id)");

		countString.append(fromString.toString());

		if (joinString.indexOf(" fetch ") != -1) {
			countString.append(joinString.toString().replaceAll(" fetch ", " "));
		} else {
			countString.append(joinString.toString());
		}

		countString.append(whereString.toString()).toString();
		appendDeleted(countString);

		if (logger.isDebugEnabled()) {
			logger.debug(countString.toString());
		}

		return countString.toString();
	}

	public String buildQueryString() {
		StringBuilder queryString = new StringBuilder().append(selectString.toString()).append(fromString.toString());
		queryString.append(joinString.toString());
		queryString.append(whereString.toString());
		appendDeleted(queryString);
		queryString.append(orderString.toString()).toString();

		if (logger.isDebugEnabled()) {
			logger.debug(queryString.toString());
		}

		return queryString.toString();
	}

	public SelectCompiled compile() {
		if (mainEntityClass == null) {
			throw new RuntimeException("主对象不能为空");
		}

		this.countStringCompile = buildCountString();
		this.selectStringCompile = buildQueryString();
		this.isComplie = true;

		SelectCompiledImpl compiled = new SelectCompiledImpl();
		compiled.setCountString(this.countStringCompile);
		compiled.setSelectString(this.selectStringCompile);
		compiled.setParameterMap(getParamMap());
		compiled.setCurPage(getCurPage());
		compiled.setFirstResult(getFirstResult());
		compiled.setMaxResults(getMaxResults());
		compiled.setLimit(isLimit());

		return compiled;
	}

	public Select from(Class<?> clazz) {
		from(clazz, null);
		return this;
	}

	/**
	 * @param clazz
	 *            如果主对象为空,那么采用clazz主助对象
	 * @param alias
	 * @return
	 */
	public Select from(Class<?> clazz, String alias) {
		if (clazz != null) {
			from(clazz.getName() + (alias != null ? " " + alias : ""));
			if (mainEntityClass == null) {
				this.mainEntityClass = clazz;
			}
		}
		return this;
	}

	public Select from(String fromString) {
		if (StringUtils.isNotBlank(fromString)) {
			if (!fromed) {
				String[] strArr = fromString.trim().split(" ");
				if (strArr.length >= 2) {
					masterEntityAlias = strArr[1];
				}
				this.fromString.append(" from ");
				fromed = true;
			}
			this.fromString.append(fromString);
		}
		return this;
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.SelectSupport#getCountString()
	 */

	public String getCountString() {
		if (!this.isComplie) {
			throw new RuntimeException("语句未被编译");
		}
		return this.countStringCompile;
	}

	/**
	 * @return the curPage
	 */
	public int getCurPage() {
		return curPage;
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.SelectSupport#getFirstResult()
	 */

	public int getFirstResult() {
		if (firstResult == 0) {
			firstResult = (curPage - 1) * getMaxResults();
			firstResult = firstResult < 0 ? 0 : firstResult;
		}
		return firstResult;
	}

	public String getFromString() {
		return fromString.toString();
	}

	public int getMaxResults() {
		return this.maxResults;
	}

	public String getOrderString() {
		return orderString.toString();
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.SelectSupport#getParameterMap()
	 */

	/**
	 * @return the pageSize
	 */
	public int getPageSize() {
		return pageSize;
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.SelectSupport#getSelectString()
	 */

	public Map<String, Object> getParameterMap() {
		return this.paramMap;
	}

	public String getSelectString() {
		if (!this.isComplie) {
			throw new RuntimeException("语句未被编译");
		}
		return this.selectStringCompile;
	}

	public Select innerJoin(String associationProperty) {
		return innerJoin(associationProperty, null);
	}

	public Select innerJoin(String associationProperty, boolean fetch) {
		return innerJoin(associationProperty, null, fetch);
	}

	public Select innerJoin(String associationProperty, String alias) {
		return innerJoin(associationProperty, alias, false);
	}

	public Select innerJoin(String associationProperty, String alias, boolean fetch) {
		return join(associationProperty, alias, "inner", fetch);
	}

	/**
	 * @return the filterDeleted
	 */
	public boolean isAutoFilterDeleted() {
		return filterDeleted;
	}

	/*
	 * (non-Javadoc)
	 * @see com.elancom.framework.dao.SelectSupport#isLimit()
	 */
	public boolean isLimit() {
		return curPage < 1;
	}

	public boolean isMaxResults() {
		return maxResults == MAX_RESULTS;
	}

	boolean isUseDistinct() {
		return this.selectString.toString().toLowerCase().indexOf("distinct") != -1;
	}

	private Select join(String associationProperty, String alias, String joinType, boolean fetch) {
		if (!fromed || StringUtils.isBlank(associationProperty)) {
			return this;
		}

		joinString.append(" ").append(joinType).append(" join ");

		if (fetch) {
			joinString.append("fetch ");
		}

		joinString.append(associationProperty);
		if (StringUtils.isNotBlank(alias)) {
			joinString.append(" ").append(alias);
		}
		return this;
	}

	public Select leftJoin(String associationProperty) {
		return leftJoin(associationProperty, null);
	}

	public Select leftJoin(String associationProperty, boolean fetch) {
		return leftJoin(associationProperty, null, fetch);
	}

	public Select leftJoin(String associationProperty, String alias) {
		return leftJoin(associationProperty, alias, false);
	}

	public Select leftJoin(String associationProperty, String alias, boolean fetch) {
		return join(associationProperty, alias, "left", fetch);
	}

	public Select order(String orderString) {
		if (StringUtils.isNotBlank(orderString)) {
			if (!ordered) {
				this.orderString.append(" order by ");
				ordered = true;
			}
			this.orderString.append(" ").append(orderString);
		}
		return this;
	}

	public Select orderIfNotNull(String orderString) {
		if (StringUtils.isNotBlank(orderString)) {
			order(orderString);
		}
		return this;
	}

	public Select rightJoin(String associationProperty) {
		return rightJoin(associationProperty, null);
	}

	public Select rightJoin(String associationProperty, boolean fetch) {
		return rightJoin(associationProperty, null, fetch);
	}

	public Select rightJoin(String associationProperty, String alias) {
		return rightJoin(associationProperty, alias, false);
	}

	public Select rightJoin(String associationProperty, String alias, boolean fetch) {
		return join(associationProperty, alias, "right", fetch);
	}

	/**
	 * @param selectString
	 * @return
	 */
	public Select select(String selectString) {
		if (StringUtils.isNotBlank(selectString)) {
			if (!selected) {
				this.selectString.append("select ");
				selected = true;
			}
			this.selectString.append(selectString);
		}
		return this;
	}

	/**
	 * @param filterDeleted
	 *            the filterDeleted to set
	 */
	public void setAutoFilterDeleted(boolean filterDeleted) {
		this.filterDeleted = filterDeleted;
	}

	/**
	 * @param curPage
	 *            the curPage to set
	 */
	public void setCurPage(int curPage) {
		this.curPage = curPage;
	}

	public void setLimit(Limit limit) {
		if (limit != null) {
			setCurPage(limit.getCurPage());
			setMaxResults(limit.getPageSize());
		}
	}

	public void setMaxResults(int max) {
		if (max < 1) {
			max = 1;
		}
		this.maxResults = max;
	}

	/**
	 * @param pageSize
	 *            the pageSize to set
	 */
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	@Override
	public String toString() {
		return "[select:" + this.selectStringCompile + "],[" + this.countStringCompile + "]";
	}

	/**
	 * @return the mainEntityClass
	 */
	public Class<?> getMainEntityClass() {
		return mainEntityClass;
	}

	/**
	 * @param mainEntityClass
	 *            the mainEntityClass to set
	 */
	public void setMainEntityClass(Class<?> mainEntityClass) {
		this.mainEntityClass = mainEntityClass;
	}
}
